using Bzstd.Utility.Assertion;

namespace Bzstd.Utility;

/// <summary>
/// 标识符命名解析器
/// </summary>
public class CaseResolver
{
	/// <summary>
	/// 字符串
	/// </summary>
	public string Origin { get; }

	/// <summary>
	/// 分割
	/// </summary>
	public List<string> Splits { get; }

	/// <summary>
	/// 过滤
	/// </summary>
	public List<string> Filtered { get; }

	/// <summary>
	/// 构造方法
	/// </summary>
	/// <param name="origin"></param>
	public CaseResolver(string origin)
	{
		Assert.NotBlank(origin, "origin不能为空");
		Origin = origin;
		Splits = new List<string>(origin.Length);
		char[] reverse = origin.Reverse().ToArray();
		string reverseString = new string(reverse);
		int current = 0;
		for (int i = 0; i < reverse.Length; i++)
		{
			char c = reverse[i];

			if (i == 0 || i == reverse.Length - 1)
			{
				if (i == 0)
				{
					if (!char.IsLetter(c) && !char.IsNumber(c))
					{
						Splits.Add(reverseString[..1]);
						current = i + 1;
					}
				}
				if (i == reverse.Length - 1)
				{
					Splits.Insert(0, reverseString[current..]);
				}
				continue;
			}

			char prev = reverse[i - 1];
			char next = reverse[i + 1];
			if (char.IsUpper(c))
			{
				if (char.IsLower(prev) || char.IsNumber(prev) || !char.IsUpper(next))
				{
					Splits.Insert(0, reverseString[current..(i + 1)]);
					current = i + 1;
				}

				continue;
			}

			if (char.IsLower(c) || char.IsNumber(c))
			{
				if (!char.IsLetter(next) && !char.IsNumber(next))
				{
					Splits.Insert(0, reverseString[current..(i + 1)]);
					current = i + 1;
				}

				continue;
			}

			if (!char.IsLetter(c) && !char.IsNumber(c))
			{
				Splits.Insert(0, reverseString[current..(i + 1)]);
				current = i + 1;
			}
		}

		Splits = Splits.ConvertAll(s => new string(s.Reverse().ToArray()));
		Filtered = Splits.Where(s => s.All(c => char.IsLetter(c) || char.IsNumber(c))).ToList();
	}

	/// <summary>
	/// Dear_Benzolamps_Artifact
	/// </summary>
	/// <returns></returns>
	public bool IsSnakeCapitalCase()
	{
		return IsSnakeCase(IsCapital);
	}

	/// <summary>
	/// dear_benzolamps_artifact
	/// </summary>
	/// <returns></returns>
	public bool IsSnakeLowerCase()
	{
		return IsSnakeCase(IsLower);
	}

	/// <summary>
	/// DEAR_BENZOLAMPS_ARTIFACT
	/// </summary>
	/// <returns></returns>
	public bool IsSnakeUpperCase()
	{
		return IsSnakeCase(IsUpper);
	}

	/// <summary>
	///
	/// </summary>
	/// <param name="predicate"></param>
	/// <returns></returns>
	public bool IsSnakeCase(Predicate<string> predicate)
	{
		return IsSplitterCase("_", predicate);
	}

	/// <summary>
	/// Dear-Benzolamps-Artifact
	/// </summary>
	/// <returns></returns>
	public bool IsKebabCapitalCase()
	{
		return IsKebabCase(IsCapital);
	}

	/// <summary>
	/// dear-benzolamps-artifact
	/// </summary>
	/// <returns></returns>
	public bool IsKebabLowerCase()
	{
		return IsKebabCase(IsLower);
	}

	/// <summary>
	/// DEAR-BENZOLAMPS-ARTIFACT
	/// </summary>
	/// <returns></returns>
	public bool IsKebabUpperCase()
	{
		return IsKebabCase(IsUpper);
	}

	/// <summary>
	///
	/// </summary>
	/// <param name="predicate"></param>
	/// <returns></returns>
	public bool IsKebabCase(Predicate<string> predicate)
	{
		return IsSplitterCase("-", predicate);
	}

	/// <summary>
	///
	/// </summary>
	/// <param name="splitter"></param>
	/// <param name="predicate"></param>
	/// <returns></returns>
	public bool IsSplitterCase(string splitter, Predicate<string> predicate)
	{
		if (Splits.Count % 2 == 0)
		{
			return false;
		}

		return Enumerable.Range(0, Splits.Count).All(i =>
		{
			if (Splits.Count % 2 == 0)
			{
				return predicate(Splits[i]);
			}
			else
			{
				return splitter == Splits[i];
			}
		});
	}

	/// <summary>
	/// dearBenzolampsArtifact
	/// </summary>
	/// <returns></returns>
	public bool IsCamelCase()
	{
		if (!Filtered.Any())
		{
			return false;
		}
		else
		{
			return IsLower(Splits[0]) && Splits.Skip(1).All(IsCapital);
		}
	}

	/// <summary>
	/// DearBenzolampsArtifact
	/// </summary>
	/// <returns></returns>
	public bool IsPascalCase()
	{
		if (!Filtered.Any())
		{
			return false;
		}
		else
		{
			return Splits.All(IsCapital);
		}
	}

	public string ToSnakeCapitalCase()
	{
		return ToSnakeCase(ToCapital);
	}

	public string ToSnakeLowerCase()
	{
		return ToSnakeCase(s => s.ToLower());
	}

	public string ToSnakeUpperCase()
	{
		return ToSnakeCase(s => s.ToUpper());
	}

	private string ToSnakeCase(Func<string, string> op)
	{
		return ToSplitterCase("_", op);
	}

	public string ToKebabCapitalCase()
	{
		return ToKebabCase(ToCapital);
	}

	public string ToKebabLowerCase()
	{
		return ToKebabCase(s => s.ToLower());
	}

	public string ToKebabUpperCase()
	{
		return ToKebabCase(s => s.ToUpper());
	}

	private string ToKebabCase(Func<string, string> op)
	{
		return ToSplitterCase("-", op);
	}

	private string ToSplitterCase(string splitter, Func<string, string> op)
	{
		return string.Join(splitter, Filtered.Select(op));
	}

	public string ToCamelCase()
	{
		if (!Filtered.Any())
		{
			return string.Empty;
		}
		else
		{
			return Filtered[0].ToLower() + string.Join(string.Empty, Filtered.Skip(1).Select(ToCapital).ToArray());
		}
	}

	public string ToPascalCase()
	{
		return ToSplitterCase(string.Empty, ToCapital);
	}

	private bool IsCapital(string str)
	{
		return char.IsUpper(str.First()) && (str.Length == 1 || str.All(c => char.IsLower(c) || char.IsLetter(c)));
	}

	private bool IsLower(string str)
	{
		return str.All(c => char.IsLower(c) || char.IsNumber(c));
	}

	private bool IsUpper(string str)
	{
		return str.All(c => char.IsUpper(c) || char.IsNumber(c));
	}

	private string ToCapital(string str)
	{
		return str[..1].ToUpper() + str[1..].ToLower();
	}
}
